package cn.ifafu.ifafu.util

import kotlin.Comparable
import kotlin.Comparator
import kotlin.Int
import kotlin.Long

object ComparatorUtils {

    /**
     * Returns a lexicographic-order comparator with another comparator.
     * If this {@code Comparator} considers two elements equal, i.e.
     * {@code compare(a, b) == 0}, {@code other} is used to determine the order.
     *
     * <p>The returned comparator is serializable if the specified comparator
     * is also serializable.
     *
     * @apiNote
     * For example, to sort a collection of {@code String} based on the length
     * and then case-insensitive natural ordering, the comparator can be
     * composed using following code,
     *
     * <pre>{@code
     *     Comparator<String> cmp = Comparator.comparingInt(String::length)
     *             .thenComparing(String.CASE_INSENSITIVE_ORDER);
     * }</pre>
     *
     * @param  other the other comparator to be used when this comparator
     *         compares two objects that are equal.
     * @return a lexicographic-order comparator composed of this and then the
     *         other comparator
     */
    fun <T> Comparator<T>.thenCompare(other: Comparator<in T>): Comparator<T> {
        return Comparator<T> { c1, c2 ->
            val res: Int = compare(c1, c2)
            if (res != 0) res else other.compare(c1, c2)
        }
    }


    /**
     * Returns a lexicographic-order comparator with a function that
     * extracts a {@code Comparable} sort key.
     *
     * @implSpec This default implementation behaves as if {@code
     *           thenComparing(comparing(keyExtractor))}.
     *
     * @param  <U>  the type of the {@link Comparable} sort key
     * @param  keyExtractor the function used to extract the {@link
     *         Comparable} sort key
     * @return a lexicographic-order comparator composed of this and then the
     *         {@link Comparable} sort key.
     * @see #comparing(Function)
     * @see #thenComparing(Comparator)
     */
    fun <T, U : Comparable<U>> Comparator<T>.thenCompare(
        keyExtractor: (T) -> U
    ): Comparator<T> {
        return thenCompare(compare(keyExtractor))
    }

    /**
     * Accepts a function that extracts a {@link java.lang.Comparable
     * Comparable} sort key from a type {@code T}, and returns a {@code
     * Comparator<T>} that compares by that sort key.
     *
     * <p>The returned comparator is serializable if the specified function
     * is also serializable.
     *
     * @apiNote
     * For example, to obtain a {@code Comparator} that compares {@code
     * Person} objects by their last name,
     *
     * <pre>{@code
     *     Comparator<Person> byLastName = Comparator.comparing(Person::getLastName);
     * }</pre>
     *
     * @param  <T> the type of element to be compared
     * @param  <U> the type of the {@code Comparable} sort key
     * @param  keyExtractor the function used to extract the {@link
     *         Comparable} sort key
     * @return a comparator that compares by an extracted key
     */
    fun <T, U : Comparable<U>> compare(
        keyExtractor: (T) -> U
    ): Comparator<T> {
        return Comparator { c1, c2 ->
            keyExtractor(c1).compareTo(keyExtractor(c2))
        }
    }

    /**
     * Accepts a function that extracts a {@code long} sort key from a type
     * {@code T}, and returns a {@code Comparator<T>} that compares by that
     * sort key.
     *
     * <p>The returned comparator is serializable if the specified function is
     * also serializable.
     *
     * @param  <T> the type of element to be compared
     * @param  keyExtractor the function used to extract the long sort key
     * @return a comparator that compares by an extracted key
     * @see #comparing(Function)
     */
    fun <T> compareLong(keyExtractor: (T) -> Long): Comparator<T> {
        return Comparator { c1, c2 ->
            keyExtractor(c1).compareTo(keyExtractor(c2))
        }
    }

}